﻿import traceback
import typing

import multiprocessing.managers
import multiprocessing.synchronize
import requestNovelsLib
import datetime
import lxml
import lxml.etree
import requests

"""
%y 两位数的年份表示（00-99）
%Y 四位数的年份表示（000-9999）
%m 月份（01-12）
%d 月内中的一天（0-31）
%H 24小时制小时数（0-23）
%I 12小时制小时数（01-12）
%M 分钟数（00-59）
%S 秒（00-59）
%a 本地简化星期名称
%A 本地完整星期名称
%b 本地简化的月份名称
%B 本地完整的月份名称
%c 本地相应的日期表示和时间表示
%j 年内的一天（001-366）
%p 本地A.M.或P.M.的等价符
%U 一年中的星期数（00-53）星期天为星期的开始
%w 星期（0-6），星期天为星期的开始
%W 一年中的星期数（00-53）星期一为星期的开始
%x 本地相应的日期表示
%X 本地相应的时间表示
%Z 当前时区的名称
%% %号本身
"""
timeFmt = "%y-%m-%d"

__runMain = False


def getNovelHome(response: requests.Response) -> typing.Union[dict[str, list[str]], None]:
	"""
	首页查找到类型与 url 的映射
	:param response: get请求之后而的响应体
	:return: 类型名称与之匹配的 url 映射
	"""
	xpath = requestNovelsLib.XpathHtml(response.text)
	## 获取 li 节点
	liNodes = xpath.xpath(r'//div[@class="second-rank-cat"]/ul/li/a')
	if xpath.resultLen() == 0:
		return
	## 类型名过滤器
	getTypeName = requestNovelsLib.getNovelTypeName()
	
	## 返回
	result: dict[str, list[str]] = {}
	## 遍历需要的类型名称
	typeNameUrl = ""
	for liNode in liNodes:
		liNodeXpathInfo = requestNovelsLib.XpathInfo(liNode)
		aTests = liNodeXpathInfo.xpath(r'./text()')
		if liNodeXpathInfo.resultLen() == 0:
			continue
		typeName = aTests[0]
		typeName = requestNovelsLib.strRemoveSpaces(typeName)
		for item in getTypeName:
			if item == typeName:
				href = liNodeXpathInfo.xpath(r'./@href')[0]
				href = href.replace(response.url, "")[1:]
				typeNameUrl = requestNovelsLib.getResponseAtStr(response, href)
				break
		if len(typeNameUrl) != 0:
			if result.get(typeName) is None:
				result[typeName] = [typeNameUrl]
			else:
				result[typeName].append(typeNameUrl)
			typeNameUrl = ""
	return result


def getPageListNovel(response: requests.Response, getCount: int, novelTypeName, novelTypeUrl, novelRootUrl) -> typing.Union[list[requestNovelsLib.NovelInfo], None]:
	"""
	获取小说信息
	若为 none 或 数量 0， 则退出这次类型的扫描
	:param response: get 之后的响应体
	:param getCount: 已经库存的数量
	:param novelTypeName: 小说类型名称
	:param novelTypeUrl: 小说类型 url
	:param novelRootUrl: 小说网站
	:return: 小说列表
	"""
	xpath = requestNovelsLib.XpathHtml(response.text)
	liNodes = xpath.xpath(r'//ul[@id="BookList"]/li')
	if xpath.resultLen() == 0:
		return None
	## 小说网址
	novelUrl: str = ""
	## 小说名称
	novelName: str = ""
	## 小说作者
	author: str = ""
	## 最后更新日期
	updateTime: str = ""
	## 最后更新选项
	lastItem: str = ""
	## 小说详情
	info: str = ""
	## 返回值
	result: list[requestNovelsLib.NovelInfo] = []
	removeStrLen = len("最新章节：")
	authorRlen = len("作者：")
	findIndex = -1
	for liNode in liNodes:
		novelUrl = novelName = novelName = author = updateTime = lastItem = info = ""
		try:
			## 小说 url 链接
			nodeXpathInfo = requestNovelsLib.XpathInfo(liNode)
			nodeTextArray = nodeXpathInfo.xpath(r'./div[@class="nrrk"]/div[@class="name"]/strong/a/@href')
			if nodeXpathInfo.resultLen() == 0:
				print(f"发现 [{novelTypeName} ]  : xxx ==== >  异常")
				continue
			novelUrl = f"{nodeTextArray[0]}"
			## 小说最后时间
			nodeTextArray = nodeXpathInfo.xpath(r'./div[@class="nrrk"]/dl/dt/em/text()')
			if nodeXpathInfo.resultLen() == 0:
				print(f"发现 [{novelTypeName} ]  : {novelUrl} ==== > 小说最后时间 异常")
				continue
			updateTime = nodeTextArray[0]
			## 时间跳出
			if not requestNovelsLib.checkUpDate(updateTime, timeFmt):
				print(f"发现 [{novelTypeName} ]  : {novelUrl} ==== > 小说最后时间 超出时间")
				continue
			## 小说名称
			nodeTextArray = nodeXpathInfo.xpath(r'./div[@class="nrrk"]/div[@class="name"]/strong/a/text()')
			if nodeXpathInfo.resultLen() == 0:
				print(f"发现 [{novelTypeName} ]  : {novelUrl} ==== > 小说名称 异常")
				continue
			novelName = nodeTextArray[0]
			## 小说最后更新章节
			nodeTextArray = nodeXpathInfo.xpath(r'./div[@class="nrrk"]/dl/dt/a/text()')
			if nodeXpathInfo.resultLen() == 0:
				print(f"发现 [{novelTypeName} ] {novelName} : {novelUrl} ==== > 小说最后更新 异常")
				continue
			lastItem = nodeTextArray[0]
			findIndex = lastItem.find("最新章节：")
			
			lastItem = lastItem[removeStrLen + findIndex:]
			
			## 小说作者
			nodeTextArray = nodeXpathInfo.xpath(r'./div[@class="nrrk"]/div[@class="name"]/span/text()')
			if nodeXpathInfo.resultLen() == 0:
				print(f"发现 [{novelTypeName} ] {novelName} : {novelUrl} ==== > 小说作者 异常")
				continue
			author = nodeTextArray[-1]
			findIndex = author.find("作者：")
			if findIndex != -1:
				author = author[findIndex + authorRlen:]
			## 小说介绍
			nodeTextArray = nodeXpathInfo.xpath(r'./div[@class="nrrk"]/p/text()')
			if nodeXpathInfo.resultLen() == 0:
				print(f"发现 [{novelTypeName} ] {novelName} : {novelUrl} ==== > 小说介绍 异常")
				continue
			info = nodeTextArray[0]
			info = info.strip()
			
			getCount += 1
			print(f"发现 [{novelTypeName} ({getCount})] {novelName} : {novelUrl} ==== >")
			
			## 添加单个对象
			result.append(requestNovelsLib.NovelInfo(novelRootUrl=novelRootUrl, novelTypeName=novelTypeName, novelTypeUrl=novelTypeUrl, novelUrl=novelUrl, novelName=novelName, author=author, updateTime=updateTime, lastItem=lastItem, info=info, updateTimeFmt=timeFmt))
		except BaseException as es:
			traceback.print_exc()
			print(f"================>\n\t{novelUrl}\n<===========")
	## 返回
	if len(result) == 0:
		return None
	return result


def getPageNextUrl(response: requests.Response, exsiNovel: int, noveList, appenLen, novelTypeName: str, novelTypeUrl: str, novelRootUrl: str) -> typing.Union[str, None]:
	"""
	获取小说翻页的下一页
	:param response: get 之后的响应体
	:param exsiNovel: 已经库存的小说
	:param noveList: 查找到的小说
	:param appenLen: 新增数量
	:param novelTypeName: 小说类型名称
	:param novelTypeUrl: 小说类型 url
	:param novelRootUrl: 小说网站
	:return: 下一页，不存在则返回 none
	"""
	if appenLen == 0:
		return None
	xpath = requestNovelsLib.XpathHtml(response.text)
	
	## 获取不正常的末尾
	# nextPageNode = xpath.xpath(r'//div[@id="pagelink"]/a[@class="last"]/@href')
	# if xpath.resultLen() == 0:
	# 	return None
	# lastPage = nextPageNode[0]
	#
	## 获取正常下一页
	nextPageNode = xpath.xpath(r'//div[@id="pagelink"]/a[@class="next"]/@href')
	if xpath.resultLen() == 0:
		return None
	nextPage = nextPageNode[0]
	
	return nextPage


def sort(novel: requestNovelsLib.NovelInfo):
	return novel.updateTime


__runTime = None


def setRunTime(time):
	global __runTime
	__runTime = time


def getRunTime():
	global __runTime
	return __runTime


def getRootUrl():
	return 'www.bequgew.com'


def modeRequestGetUrl(lock: multiprocessing.synchronize.Lock = None,workerThreadNumber=0, nextRequestTimeSece=0.5, titleSleepTime=10):

	global __runMain
	if not __runMain:
		if lock is None:
			raise RuntimeError("请在调用时候设置它的进程锁")
	else:
		if lock is None:
			lock = multiprocessing.Lock()
	
	rootUrl = getRootUrl()
	requestUrl = requestNovelsLib.ResponseNovel(f'https://{rootUrl}/')
	newFolder = requestNovelsLib.getUrlTargetFolder(rootUrl)
	try:
		## 获取类型类型的请求页面
		runEndTime = requestNovelsLib.ayscRequestGetUrl(requestUrl, getNovelHome, getPageListNovel, getPageNextUrl, rootUrl, newFolder, __runMain, proLock=lock, nextRequestTimeSece=nextRequestTimeSece, workerThreadNumber=workerThreadNumber, titleSleepTime=titleSleepTime)
		setRunTime(runEndTime)
	except:
		traceback.print_exc()
	return
	
if __name__ == '__main__':
	__runMain = True
	requestNovelsLib.initLib()
	modeRequestGetUrl()